package hyl.android.redux.reducer;


import java.lang.reflect.Method;
import java.security.InvalidParameterException;
import java.util.concurrent.ConcurrentHashMap;

import hyl.android.redux.action.Action;
import hyl.android.redux.annotation.ActionType;
import hyl.android.redux.state.State;


/**
 * User: HuangYunLin(283857754@qq.com)
 * Date: 2016-04-22
 * Time: 09:49
 * FIXME
 */
public class BaseReducer<A extends Action, S extends State> implements Reducer<A, S> {

    protected final ConcurrentHashMap<Integer, Method> mActionMap;

    public BaseReducer() {
        mActionMap = new ConcurrentHashMap<>();
        Method[] methods = this.getClass().getMethods();//get only public methods
        for (Method m : methods) {
            if (m.isAnnotationPresent(ActionType.class)) {

                String methodName = m.getName();

                ActionType actionAnnotation = m.getAnnotation(ActionType.class);
                int        actionName       = actionAnnotation.value();

                Class<?>[] parameterTypes = m.getParameterTypes();

                if (parameterTypes.length != 2)
                    throw new InvalidParameterException(String.format("Bound method '%s' must accept t: State and Objectwo arguments", methodName));

                if (m.getReturnType().equals(Void.TYPE))
                    throw new InvalidParameterException(String.format("Bound method '%s' must return an instance of the State", methodName));

                bindAction(actionName, m);
            }
        }
    }

    protected void bindAction(int actionType, Method method) {
        mActionMap.put(actionType, method);
    }

    @Override
    public S call(A action, S state) {

        try {
            if (mActionMap.containsKey(action.type)) {
                Method     method         = mActionMap.get(action.type);
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (action.getClass().getName().equalsIgnoreCase(parameterTypes[0].getName())) {
                    return (S) method.invoke(this, action, state);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        return state;
    }
}
